package utils.webdav;

import com.github.sardine.DavResource;
import com.github.sardine.Sardine;
import com.github.sardine.SardineFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.InputStreamEntity;
import org.apache.jackrabbit.webdav.DavConstants;
import org.apache.jackrabbit.webdav.DavServletResponse;
import org.apache.jackrabbit.webdav.client.methods.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;

@Slf4j
public class WebDavTool {
    private static final HttpClient client = WebDavInitUtil.initHttpClient();
    private static final HttpClientContext context = WebDavInitUtil.initContext();
    private static final WebDavConfig config = WebDavConfig.getInstance();
    private static final Sardine sardine = SardineFactory.begin(config.getUserName(), config.getPassWord());
    private static final WebDavTool instance = new WebDavTool();
    private static final String TAG = "WebDav：";

    private WebDavTool() {
    }

    public static WebDavTool getInstance() {
        return instance;
    }

    /**
     * 根据路径获取路径下所有文件
     *
     * @param resourcePath 路径
     * @return 文件数据集合
     * @throws IOException 抛出IO异常
     */
    public List<DavResource> getResourceList(String resourcePath) throws IOException {
        return sardine.list(resourcePath);
    }

    /**
     * 上传文件
     *
     * @param filePath 文件绝对路径
     * @param file     文件对象
     * @return 返回上传后的文件URI地址
     */
    public String upload(String filePath, File file) {
        log.info(TAG + " 文件名：" + file.getName() + " 状态：开始上传。");
        String newFilePath;
        if (filePath.startsWith(config.getBaseURL())) {
            newFilePath = filePath;
        } else {
            newFilePath = config.getBaseURL() + filePath;
        }

        try {
            if (!existDir(newFilePath)) {
                // 不存在目录则创建
                mkDirs(newFilePath);
            }
            String fileUri = newFilePath + file.getName();
            HttpPut put = new HttpPut(fileUri);
            InputStreamEntity entity = new InputStreamEntity(new FileInputStream(file), file.length());
            put.setEntity(entity);
            int status = execute(put);
            if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) {
                log.info(TAG + " 文件名：" + file.getName() + " 状态：成功上传。");
                return fileUri;
            }
        } catch (IOException e) {
            log.info(TAG + " 文件名：" + file.getName() + " 状态：上传失败。");
        }
        return null;
    }

    /**
     * 根据路径创建目录
     *
     * @param dir 目录绝对路径
     * @throws IOException 抛出IO异常
     * @return 创建目录的响应代码
     */
    public Integer mkdir(String dir) throws IOException {
        HttpMkcol mkcol = new HttpMkcol(dir);
        int retCode = execute(mkcol);
        log.info(TAG + " 目录：" + dir + " 状态：" + (retCode == 201 ? "创建成功。" : "创建失败。"));
        return retCode;
    }

    /**
     * 创建多层文件夹
     *
     * @param path 路径
     * @throws IOException 抛出IO异常
     */
    public void mkDirs(String path) throws IOException {
        String temDir = path;
        if (path.startsWith(config.getBaseURL())) {
            temDir = path.replaceAll(config.getBaseURL(), "");
        }
        String[] dirs = temDir.split("/");
        String uri = config.getBaseURL();
        for (String dir : dirs) {
            uri = uri + dir + "/";
            if (existDir(uri)) {
                log.info(TAG + " 目录：" + uri + " 状态：已存在。");
                continue;
            }
            if (mkdir(uri) != HttpStatus.SC_CREATED) {
                return;
            }
        }
    }

    /**
     * 删除文件
     *
     * @param filePath 文件绝对路径
     * @throws IOException 抛出IO异常
     */
    public void delete(String filePath) throws IOException {
        HttpDelete delete = new HttpDelete(filePath);
        int status = execute(delete);
        log.info(TAG + "删除文件：" + filePath + " 返回值：" + status);
    }

    /**
     * 移动文件
     *
     * @param fileSourcePath  源文件路径
     * @param destinationPath 复制后的路径
     * @throws IOException 抛出IO异常
     */
    public void move(String fileSourcePath, String destinationPath) throws IOException {
        HttpMove httpMove = new HttpMove(fileSourcePath, destinationPath, true);
        //判断文件是否存在
        if (existDir(fileSourcePath)) {
            //执行移动文件操作
            int statusCode = execute(httpMove);
            log.info(TAG + "移动文件，返回值：" + statusCode);

        } else {
            //文件不存在，返回提示
            log.info(TAG + "要移动的文件不存在");
        }
    }

    /**
     * 复制文件
     *
     * @param fileSourcePath  源文件路径
     * @param destinationPath 复制后的路径
     * @throws IOException 抛出IO异常
     */
    public void copy(String fileSourcePath, String destinationPath) throws IOException {
        HttpCopy httpCopy = new HttpCopy(fileSourcePath, destinationPath, true, true);
        //判断文件是否存在
        if (existDir(fileSourcePath)) {
            //执行复制文件操作
            int statusCode = execute(httpCopy);
            log.info(TAG + "复制文件，返回值：" + statusCode);
        } else {
            //文件不存在，返回提示
            log.info(TAG + "要复制的文件不存在");
        }
    }

    /**
     * 判断文件或目录是否存在
     *
     * @param path 文件或目录路径
     * @return Bool
     * @throws IOException 抛出异常
     */
    public Boolean existDir(String path) throws IOException {

        HttpPropfind propfind = new HttpPropfind(path, DavConstants.PROPFIND_ALL_PROP, 1);
        int statusCode = execute(propfind);
        log.info(TAG + "判断路径：" + path + " 状态：" + (statusCode == 207 ? "存在。" : "不存在。"));
        return DavServletResponse.SC_MULTI_STATUS == statusCode;
    }

    private int execute(HttpUriRequest request) throws IOException {
        return client.execute(request, context).getStatusLine().getStatusCode();
    }
}